package com.wang.transfer.util.springBean.beanConfig;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;

/**
 * 对象工厂，我们最核心的一个类，在它初始化的时候，创建了bean注册器，完成了资源的加载。
 * 获取bean的时候，先从单例缓存中取，如果没有取到，就创建并注册一个bean
 */
public class BeanFactory {

    private final Logger logger = Logger.getLogger(BeanFactory.class.getName());

    private final Map<String, BeanDefinition> beanDefinitionMap;

    public final BeanRegister beanRegister;

    public BeanFactory() {
        // 创建bean注册器
        beanRegister = new BeanRegister();
        // 加载资源
        this.beanDefinitionMap = ResourceLoader.getResource();
        // 加载所有的类
//        Iterator<String> iterator = beanDefinitionMap.keySet().iterator();
//        if (iterator.hasNext()) {
//            createBean(beanDefinitionMap.get(iterator.next()));
//        }
    }

    /**
     * 获取bean
     * @param beanName bean名称
     * @return
     */
    public Object getBean(String beanName) {
        // 从bean缓存中取
        Object bean = beanRegister.getSingletonBean(beanName);
        if (bean != null) {
            return bean;
        }
        // 根据bean定义，创建bean
        return createBean(beanDefinitionMap.get(beanName));
//        return null;
    }

    /**
     * 创建Bean
     * @param beanDefinition bean定义
     * @return
     */
    private Object createBean(BeanDefinition beanDefinition) {
        try {
            // 获取、创建单例bean对象
            Class bean = beanDefinition.getBeanClass();
            logger.info("Type: " +bean);
            if (bean.isInterface()) {
                // 如果是接口类，则查找他的实现类
                ArrayList<Class> allClassByInterface = getAllClassByInterface(bean);
                for (Class aClass : allClassByInterface) {
                    // 父类.class.isAssignableFrom(子类.class)
                    if (bean.isAssignableFrom(aClass)) {
                        Object b = aClass.newInstance();
                        // 缓存Bean
                        beanRegister.registerSingletonBean(b.getClass().getName(), b);
                        return beanRegister.getSingletonBean(b.getClass().getName());
                    }
                }
            } else {
                Object b = beanDefinition.getBeanClass().newInstance();
                beanRegister.registerSingletonBean(beanDefinition.getBeanName(), b);
                return beanRegister.getSingletonBean(beanDefinition.getBeanName());
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取接口类所有的实现类
     * @param clazz 接口类
     * @return 实现类
     */
    private ArrayList<Class> getAllClassByInterface(Class clazz) {
        ArrayList<Class> list = new ArrayList<>();
        // 判断是否是一个接口
        if (clazz.isInterface()) {
            try {
                ArrayList<Class> allClass = getAllClass(clazz.getPackage().getName());
                // 循环判断路径下的所有类是否实现了指定的接口，并且排除接口类自己
                for (Class aClass : allClass) {
                    // 判断是否是同一个接口
                    // isAssignableFrom:判定此Class对象所表示的类或接口与指定的Class参数所表示的类或接口是否相同，或是否是其超类或超接口
                    if (!clazz.equals(aClass)) {
                        // 自身并不加里面
                        list.add(aClass);
                    }
                }
            } catch (Exception e) {
                logger.info("出现异常： " + e.getMessage());
                throw new RuntimeException("出现异常： " + e.getMessage());
            }
        }
        logger.info("class list size: " + list.size());
        return list;
    }

    /**
     * 从一个指定的路径下查找所有的类
     * @param packageName 包名
     * @return 所有类
     */
    public ArrayList<Class> getAllClass(String packageName) {
        logger.info("packageName to search: " + packageName);
        List<String> classNameList = getClassName(packageName);
        ArrayList<Class> list = new ArrayList<>();
        for (String className : classNameList) {
            try {
                list.add(Class.forName(className));
            } catch (ClassNotFoundException e) {
                logger.info("load class form name failed: " + className + e.getMessage());
                throw new RuntimeException("load class form name failed: " + className + e.getMessage());
            }
        }
        logger.info("find list size: " + list.size());
        return list;
    }


    /**
     * 获取某包下所有类
     * @param packageName 包名
     * @return 类的完整名称
     */
    private List<String> getClassName(String packageName) {
        List<String> fileNames = null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath = packageName.replace(".", "/");
        URL url = loader.getResource(packagePath);
        if (url != null) {
            String type = url.getProtocol();
            logger.info("file type: " + type);
            if (type.equals("file")) {
                String fileSearchPath = url.getPath();
                logger.info("fileSearchPath: " + fileSearchPath);
                // 打包文件路径
//                fileSearchPath = fileSearchPath.substring(0, fileSearchPath.indexOf("/out"));
                fileSearchPath = fileSearchPath.substring(0, fileSearchPath.indexOf("/springBean") + 11);
                logger.info("newFileSearchPath: " + fileSearchPath);
                fileNames = getClassNameByFile(fileSearchPath);
            } else if (type.equals("jar")) {
                try {
                    JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                    JarFile jarFile = jarURLConnection.getJarFile();
                    fileNames = getClassNameByJar(jarFile, packagePath);
                } catch (IOException e) {
                    throw new RuntimeException("open Package URL failed: " + e.getMessage());
                }
            } else {
                throw new RuntimeException("file system not support! cannot load MsgProcessor!");
            }
        }
        return fileNames;
    }


    /**
     * 从项目文件获取某包下所有类
     * @param filePath 文件路径
     * @return 类的完整名称
     */
    private List<String> getClassNameByFile(String filePath) {
        List<String> myClassName = new ArrayList<>();
        File file = new File(filePath);
        File[] childFiles = file.listFiles();
        for (File childFile: childFiles) {
            if (childFile.isDirectory()) {
                myClassName.addAll(getClassNameByFile(childFile.getPath()));
            } else {
                String childFilePath = childFile.getPath();
                if (childFilePath.endsWith(".class")) {
                    childFilePath = childFilePath.substring(childFilePath.indexOf("\\com") + 1, childFilePath.lastIndexOf("."));
                    childFilePath = childFilePath.replace("\\", ".");
                    myClassName.add(childFilePath);
                    logger.info("childFilePath:" + childFilePath);
                }
            }
        }
        return myClassName;
    }


    /**
     * 从jar获取某包下所有类
     * @param jarFile
     * @param packagePath
     * @return 类的完整名称
     */
    private List<String> getClassNameByJar(JarFile jarFile, String packagePath) {
        List<String> myClassName = new ArrayList<>();
        try {
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String name = jarEntry.getName();
                logger.info("entries jarFile" + name);

                if (name.endsWith(".class")) {
                    name = name.replace("/",".").substring(0,name.lastIndexOf("."));
                    myClassName.add(name);
                    logger.info("Find Class" + name);
                }
            }
        } catch (Exception e) {
            logger.info("发生异常：" + e.getMessage());
            throw new RuntimeException("发生异常：" + e.getMessage());
        }
        return myClassName;
    }
}
